<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- <meta http-equiv="Content-Type" content="text/html" /> -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>3n + 1 Game in Byte</title>
  </head>
  <style>
    :root {
      --FONT-SIZE: 18px;
      --C-WHITE: #f3f3f3;
      --C-BLACK: #333;
      --C-RED: #f99;
      --C-GREEN: #9f9;
      --C-BLUE: #99f;
      --C-GRAY: #666;
      /* 
      --C-CYAN: #9ff;
      --C-PURPLE: #f3f;
      --C-YELLOW: #ff9;
      */
    }

    body {
      overflow: scroll;
      background-color: var(--C-BLACK);
      color: var(--C-WHITE);
      font-family: Consolas, "Courier New", monospace;
    }

    p {
      text-align: center;
    }

    #rows {
      display: flex;
      flex-flow: column nowrap;
      /* place-content: start end; */
      place-items: start start;
      width: 100%;
      margin: 0 auto;
      padding: 0;

      .row {
        display: flex;
        min-width: 30em;
        margin: 0.5em 0 0.5em 0.5em;
        flex-flow: row nowrap;
        /* place-items: center center; */
        place-content: center right;
        font-size: var(--FONT-SIZE);

        .box {
          .next {
            color: var(--C-GREEN);
          }
          .prev {
            color: var(--C-BLUE);
          }
          .brow {
            color: var(--C-RED);
          }

          /* &.yellow {
            background-color: var(--C-YELLOW);
          }
          &.purple {
            background-color: var(--C-PURPLE);
          }
          &.cyan {
            background-color: var(--C-CYAN);
          } */
          &.gray{
            background-color: var(--C-GRAY);
          }
        }

        &.org {
          background-color: var(--C-WHITE);
          .box {
            color: var(--C-BLACK);
          }
        }
      }
    }
  </style>

  <body>
    <p><input type="number" id="ipt" placeholder="Input Dec Number" /></p>
    <div id="rows"></div>
  </body>

  <script>
    "use strict";

    const MAX_NUM = 1 << 20;
    const objSum = (obj) => Object.values(obj).reduce((a, b) => a + b, 0);
    const Boxes = {
      n: [],
      p: [],
      b: [],
      get debug() {
        return `\n ${Boxes.n.join("")} \n ${Boxes.p.join("")} \n ${Boxes.b.join(
          ""
        )} \n`;
      },
      at(i) {
        return { n: Boxes.n.at(i), p: Boxes.p.at(i), b: Boxes.b.at(i) };
      },
      pop: () => {
        Boxes.n.pop();
        Boxes.p.pop();
        Boxes.b.pop();
      },
      unshift: ({ n, p, b }) => {
        Boxes.n.unshift(n);
        Boxes.p.unshift(p);
        Boxes.b.unshift(b);
      },
    };

    const rows = document.getElementById("rows");

    const drowBoxRow = (isOrg = false, idx = null) => {
      let row = document.createElement("div");
      row.className = isOrg ? "row org" : "row";

      if (isOrg) {
        for (let i = 0; i < Boxes.n.length; i++) {
          let box = document.createElement("div");
          box.className = "box";
          let div = document.createElement("div");
          div.textContent = Boxes.n[i];
          box.appendChild(div);
          row.appendChild(box);
        }
      } else {
        for (let i = 0; i < Boxes.n.length; i++) {
          let box = document.createElement("div");
          box.className = idx === i ? "box gray" : "box";

          let next = document.createElement("div");
          next.className = "next";
          next.textContent = Boxes.n[i];
          box.appendChild(next);

          let prev = document.createElement("div");
          prev.className = "prev";
          prev.textContent = Boxes.p[i];
          box.appendChild(prev);

          let brow = document.createElement("div");
          brow.className = "brow";
          brow.textContent = Boxes.b[i];
          box.appendChild(brow);

          row.appendChild(box);
        }
      }

      rows.appendChild(row);
    };

    document.getElementById("ipt").addEventListener("change", (evn) => {
      let num = Number.parseInt(evn.target.value);
      if (Number.isNaN(num)) return alert("Error Input");
      if (num < 0 || num >= MAX_NUM) return alert("Out of Range");

      rows.replaceChildren();

      Boxes.n = [0, 0].concat(Array.from(num.toString(2)).map((e) => +e));
      let l = Boxes.n.length;

      Boxes.p = new Array(l).fill(null);
      Boxes.b = new Array(l).fill(null);

      let d = 0;
      while (l >= 2) {
        while (!objSum(Boxes.at(-1))) {
          Boxes.pop();
          l--;
          drowBoxRow(!1, l - 1);
          if (l === 1) return;
        }

        Boxes.p[l - 1] = 0;
        Boxes.b[l - 1] = 1;
        drowBoxRow(!1, l - 1);

        for (let i = 1,v = 0; i <= l; i++) {
          
          v = Boxes.n[l - i] + Boxes.p[l - i] + Boxes.b[l - i];

          Boxes.p[l - i - 1] = Boxes.n[l - i];
          Boxes.b[l - i - 1] = (v & 2) >> 1;
          Boxes.n[l - i] = v & 1;
          Boxes.p[l - i] = Boxes.b[l - i] = 0;

          if(Boxes.p[l - i - 2] === null) {
            break;
          }
          
          drowBoxRow(!1,l - i -1);
        }

      }
    });
  </script>
</html>
